angular.module('furion.services.utils', [])

.factory "H5PostMessage", ()->
  {
    init: (scope, target_window, target_url)->
      # scope._pmdata = {}

      window.addEventListener 'message', (e)->
        console.debug typeof e.data
        switch typeof e.data
          when 'string'
            switch e.data
              when 'get_account'
                if target_url
                  target_window.postMessage(JSON.stringify({account: furion.storage.account()}), '*')
          # when 'object'
          #   scope.$apply ->
              # scope._pmdata = e.data
      return
  }

.factory "TouchidService", ($rootScope, $cordovaTouchID, $state, Toast, ModalManager, GestureService) ->
  obj = {}

  # 一开始就已经检查好，这样就不需要异步来检查
  obj.isDeviceSupport = ()->
    return 'true' == window.localStorage.getItem('touchid-is-support')

  obj.last_ts = ()->
    ts = window.localStorage.getItem('rt-gesture_prev_lock_ts')
    return if ts? then parseInt(ts) else 0

  # 检查设备是否支持指纹
  obj.checkDeviceSupport = (callback)->
    if typeof(touchid) == 'undefined' # 不支持插件
      window.localStorage.setItem('touchid-is-support', false)
      callback() if callback
    else # 支持插件
      $cordovaTouchID.checkSupport().then () ->
        window.localStorage.setItem('touchid-is-support', true)
        callback() if callback
      , (error) ->
        window.localStorage.setItem('touchid-is-support', false)
        callback() if callback

  # 检查是否需要打开指纹验证
  obj.needOpen = (ignore_time)->
    return false unless obj.isDeviceSupport() # 如果设备不支持
    return false unless furion.storage.isLogin() # 如果用户没登录
    return false unless furion.storage.account().touchid_is_enabled # 如果用户没打开指纹

    unless ignore_time
      delta = (new Date()).getTime() - obj.last_ts()
      return false if delta < 30 * 1000 # 30s 内不需要 touchid

    # 记录当前页面，解锁后还需要跳回去
    url = window.location.href
    current_url = url[url.indexOf('#')..url.length-1]
    if "#/gesture-login" != current_url
      window.localStorage.setItem('rt-gesture_prev_url', current_url)

    return true

  obj.can_suggest = ()->
    return false unless obj.isDeviceSupport() # 如果设备不支持
    return false unless furion.storage.isLogin() # 如果用户没登录
    return true

  # 打开验证
  obj.authenticate = () ->
    # 关闭之前所有modal
    $rootScope.$emit 'close-all-modals'

    $rootScope.$emit 'modals:open', {modal_name: 'touchid'}

    # 先跳转到预先页面
    prev_url = window.localStorage.getItem('rt-gesture_prev_url')
    if !prev_url || prev_url == '#/welcome'
      $state.go 'app.home'
    else
      window.location.assign prev_url

    $cordovaTouchID.authenticate('请把手指放在Home键上验证指纹').then () ->
      Toast.show '指纹验证成功'
      $rootScope.$emit 'modals:close', {modal_name:'touchid'}
    , (error) ->
      console.debug arguments
      error = obj.error_format error
      Toast.show "指纹验证失败，#{error}"
      $rootScope.$emit 'modals:close', {modal_name:'touchid'}

      if GestureService.needOpen()
        GestureService.authenticate()
      else
        furion.storage.deleteAccount()
        $rootScope.$emit 'modals:open', {modal_name: 'signin'}
        $state.go 'app.home'

  obj.error_format = (error)->
    {
      'authenticationFailed': '身份验证失败'
      'userCancel': '用户点击取消按钮'
      'userFallback': '用户选择输入密码'
      'systemCancel': '系统取消授权'
      'passcodeNotSet': '系统未设置密码'
      'touchIDNotAvailable': 'touchid在设备上不可用'
      'touchIDNotEnrolled': '没有设置指纹密码'
      'unknown': '未知错误'
    }[error] || error

  return obj

.factory( "GestureService", ($q, $rootScope, $timeout, $state)->
  obj = {
    needNotification: ()-> #1. 没设手势，2. 没提醒过
      console.log("[need notification] pattern"+this.getPattern())
      console.log("[need notification] notified"+this.isNotified())
      if not this.getPattern() and not this.isNotified()
        return true
      else
        return false
    setNotified: () ->
      window.localStorage.setItem('rt-gesture_setting_notified', true)
    isNotified: () ->
      return "true" == window.localStorage.getItem('rt-gesture_setting_notified')
    enable: () ->
      window.localStorage.setItem('rt-gesture_enabled', true)
    disable: () ->
      window.localStorage.setItem('rt-gesture_enabled', false)
    isEnabled: () ->
      return "true" == window.localStorage.getItem('rt-gesture_enabled')
    # touchidIsEnable: ()->
    #   window.localStorage.getItem('touchid-is-support')
    touchidIsEnable: null
    getAccount: () ->
      oaa = window.localStorage.getItem('rt-account')
      if oaa and JSON.parse(oaa).phone
        return ""+JSON.parse(oaa).phone
      else
        return '/check_app_version'
    getPattern: () ->
      return window.localStorage.getItem('rt-gesture_pattern')
    resetPattern: () ->
      this.disable()
      window.localStorage.removeItem('rt-gesture_setting_notified')
      return window.localStorage.removeItem('rt-gesture_pattern')
    setPattern: (pattern) ->
      window.localStorage.setItem('rt-gesture_pattern', this.encPattern(pattern))
      this.enable()
    updatePattern: (pattern, gesture_enabled) -> # direct from server, no enc
      if pattern
        console.log("update gesture according to server")
        window.localStorage.setItem('rt-gesture_pattern', (pattern))
        if "true" == gesture_enabled or true == gesture_enabled
          this.enable()
      else
        console.log("no gesture set on server, reset local")
        this.resetPattern()

    checkLimit: (pattern)->
      pattern && pattern.length > 3 && pattern.length < 10

    encPattern: (pattern) ->
      return md5(pattern+this.getAccount())
    checkPattern: (pattern) ->
      deferred = $q.defer()
      promise = deferred.promise

      promise.success = (fn) ->
        promise.then(fn)
        return promise
      promise.error = (fn) ->
        promise.then(null, fn)
        return promise

      if (this.encPattern(pattern) == this.getPattern())
        window.localStorage.setItem('rt-gesture_reject_time', 0)

        deferred.resolve()
      else
        # add reject time
        grt = parseInt window.localStorage.getItem('rt-gesture_reject_time')
        if grt && grt > 0
          window.localStorage.setItem('rt-gesture_reject_time', grt + 1)
          console.debug "gesture error time: #{grt + 1}"
        else
          window.localStorage.setItem('rt-gesture_reject_time', 1)
          console.debug "gesture error time: 1"

        # waiting when gesture_reject_time > limit
        if grt >= 4
          window.localStorage.setItem('rt-gesture_reject_waiting', 60)
          this.startRejectWaiting()

        deferred.reject()

      return promise

    stop_reject_flag: false
    stopReject: ()->
      this.stop_reject_flag = true
      this.setRejectWaitingHandler(0)
      window.localStorage.setItem('rt-gesture_reject_time', 0)

    startRejectWaiting: ()->
      this.stop_reject_flag = false
      waiting = parseInt window.localStorage.getItem('rt-gesture_reject_waiting')
      if waiting && waiting > 0
        console.debug "start geature reject waiting: #{waiting}"
        $rootScope.gesture_reject_waiting = waiting
        gesture_service = this
        for i in [1..waiting]
          t = 1000 * i
          past = 1
          $timeout ->
            return if gesture_service.stop_reject_flag
            s = waiting - past
            if s >= 0
              gesture_service.setRejectWaitingHandler(s)
              console.debug "gesture reject waiting: #{$rootScope.gesture_reject_waiting}"
            past += 1
          , t
      return

    setRejectWaitingHandler: (sec)->
      window.localStorage.setItem('rt-gesture_reject_waiting', sec)
      $rootScope.gesture_reject_waiting = sec

    last_ts: ()->
      ts = window.localStorage.getItem('rt-gesture_prev_lock_ts')
      return if ts? then parseInt(ts) else 0
  }

  obj.checkLockPattern = (check_time = true)->
    # Toast.show String(furion.utils.isDevice())
    # return false unless furion.utils.isDevice()
    return false unless furion.storage.isLogin()
    return false unless obj.isEnabled()
    return false unless !obj.touchidIsEnable
    # return false unless !TouchidService.isEnabled() && !TouchidService.touchid_support
    if check_time
      delta = (new Date()).getTime() - obj.last_ts()
      return false if delta < 60 * 1000 # 2分钟内不需要手势密码
      url = window.location.href
      current_url = url[url.indexOf('#')..url.length-1]
      if "#/gesture-login" != current_url
        window.localStorage.setItem('rt-gesture_prev_url', current_url)
    else
      window.localStorage.removeItem('rt-gesture_prev_url')
    $rootScope.$emit 'close-all-modals'
    window.location.assign "#/gesture-login"
    return true

  obj.needOpen = (ignore_time)->
    return false unless furion.storage.isLogin() # 如果用户没登录
    return false unless furion.storage.account().gesture_enabled # 如果用户没打开

    unless ignore_time
      delta = (new Date()).getTime() - obj.last_ts()
      return false if delta < 30 * 1000 # 30s 内不需要手势

    # 记录当前页面，解锁后还需要跳回去
    url = window.location.href
    current_url = url[url.indexOf('#')..url.length-1]
    if "#/gesture-login" != current_url
      window.localStorage.setItem('rt-gesture_prev_url', current_url)

    return true

  obj.authenticate = ()->
    $rootScope.$emit 'close-all-modals'
    window.location.assign "#/gesture-login"

  obj.can_suggest = ()->
    return false unless furion.storage.isLogin() # 如果用户没登录
    return false if obj.getPattern() # 如果用户已设置
    return true

  obj
)

.factory( "TouchAndGestureService", (TouchidService, GestureService, RemoteSrv, Toast, $cordovaTouchID, $rootScope, $state)->
  obj = {}

  obj.check = (ignore_time=false)->
    if TouchidService.needOpen(ignore_time) # 优先指纹
      TouchidService.authenticate()
    else if GestureService.needOpen(ignore_time)
      GestureService.authenticate()

  obj.suggestGuesture = ()->
    if GestureService.can_suggest()
      $rootScope.$emit 'close-all-modals'
      $state.go 'gesture-password'
    else
      $rootScope.$emit 'close-all-modals'
      $state.go 'app.home', {}, {reload: true}


  obj.suggestTouchidFail = (error)->
    # 发送取消指纹的 api 即可不需返回值
    RemoteSrv.setTouchid({touchid_enabled: false})
    obj.suggestGuesture()
    return

  # 用户登录／注册时推荐用户设置指纹／手势
  obj.suggestUserSetting = ()->
    if TouchidService.can_suggest() # 可以设置指纹
      $rootScope.$emit 'close-all-modals'
      $rootScope.$emit 'modals:open', {modal_name: 'touchid'}

      $cordovaTouchID.authenticate('您可以使用Touch ID登录，请把手指放在Home键上验证指纹，设置指纹登录功能').then( () ->
        RemoteSrv.setTouchid({touchid_enabled: true}).done (resp) ->
          furion.storage.updateAccount resp.data.account
          Toast.show '指纹登录开启成功'
          $rootScope.$emit 'close-all-modals'
          $state.go 'app.home', {}, {reload: true}
      , obj.suggestTouchidFail)
    else
      obj.suggestGuesture()

  return obj
)



.factory "Toast", ($rootScope, $timeout, $ionicPopup, $cordovaToast)->
  promise_popup = null
  promise_timeout = null
  {
    show: (message, duration, position)->
      message = message || "出错了..."
      duration = duration || "short"
      position = position || "center"
      if window.plugins && window.plugins.toast
        $cordovaToast.show(message, duration, position)
      else
        duration = if duration == "short" then 2000 else 5000

        # refresh toast message when is opening
        $rootScope._toast_message = message

        if promise_popup
          # clear the timeout setted (reset)
          $timeout.cancel promise_timeout if promise_timeout
        else
          promise_popup = $ionicPopup.alert
            template: "<div class='toast'>{{_toast_message}}</div>"
            scope: $rootScope
            buttons: []

        # set timeout
        promise_timeout = $timeout ()->
          promise_popup.close()
          promise_popup = null
          delete $rootScope._toast_message
        , duration
  }

.factory '$debounce', ($rootScope, $browser, $q, $exceptionHandler)->
  deferreds = {}
  methods = {}
  uuid = 0

  debounce = (fn, delay, invokeApply)->
    deferred = $q.defer()
    promise = deferred.promise
    skipApply = (angular.isDefined(invokeApply) && !invokeApply)
    timeoutId = false
    cleanup = false
    methodId = false
    bouncing = false

    # check we dont have this method already registered
    angular.forEach methods, (value, key)->
      if(angular.equals(methods[key].fn, fn))
        bouncing = true
        methodId = key

    # not bouncing, then register new instance
    if !bouncing
      methodId = uuid++
      methods[methodId] = {fn: fn}
    else
      # clear the old timeout
      deferreds[methods[methodId].timeoutId].reject('bounced')
      $browser.defer.cancel(methods[methodId].timeoutId)

    debounced = ()->
      # actually executing? clean method bank
      delete methods[methodId]

      try
        deferred.resolve fn()
      catch e
        deferred.reject e
        $exceptionHandler e

      if !skipApply
        $rootScope.$apply()

    timeoutId = $browser.defer(debounced, delay)

    # track id with method
    methods[methodId].timeoutId = timeoutId

    cleanup = (reason)->
      delete deferreds[promise.$$timeoutId]

    promise.$$timeoutId = timeoutId
    deferreds[timeoutId] = deferred
    promise.then(cleanup, cleanup)

    return promise


  # similar to angular's $timeout cancel
  debounce.cancel = (promise)->
    if (promise && promise.$$timeoutId in deferreds)
      deferreds[promise.$$timeoutId].reject('canceled')
      return $browser.defer.cancel(promise.$$timeoutId)

    return false

  return debounce

.factory 'AuthNotify', ($rootScope)->
  obj = {}

  obj.notifyAuth = ()-> 
    if furion.storage.isLogin() # 已登录状态下
      if not obj.isNotifiedAuth() and not obj.checkAuthenticated() # 未认证且未提示过认证
        $rootScope.$emit 'modals:open', {modal_name: 'goAuth'}
      else
        console.debug 'already notified'
    else
      console.debug 'not login ever'
    obj.setNotifiedAuth() # 设置已提醒

  obj.checkAuthenticated = () ->
    if furion.storage.account().authenticated
      return true 
    else 
      return false

  obj.setNotifiedAuth = () ->
    window.localStorage.setItem('rt-auth_notity_notified', true)

  obj.resetNotifiedAuth = () ->
    window.localStorage.removeItem('rt-auth_notity_notified')
    console.debug 'remove notifyAuth'

  obj.isNotifiedAuth = () ->
    return "true" == window.localStorage.getItem('rt-auth_notity_notified')

  return obj